
// A module written to create the arctan fucntion!
// This module registers in the input data when start_in goes high. 
// It then processes until the phase result is finished and
// outputs a done signal.

module fcl_arctan
	//------------------------------------------------------
	//--				External Parameters			 	  --
	//------------------------------------------------------
#(
    parameter integer 	CORDIC_WIDTH = 16
)
	
	//------------------------------------------------------
	//--					Ports					 	  --
	//------------------------------------------------------
(
	// System Signals
    input 	wire			_reset_in,
    input 	wire			clk_in,
	
	input 	wire										start_in,
    input 	wire	signed		[(CORDIC_WIDTH-1):0]	x_data_in,
    input 	wire	signed		[(CORDIC_WIDTH-1):0]	y_data_in,
    output 	wire										done_out,
    output 	wire				[(CORDIC_WIDTH-1):0]	q_data_out
);

	localparam	integer COUNT_WIDTH = clogb2(CORDIC_WIDTH + 3);

	genvar	i;
	
	
	reg		signed	[(CORDIC_WIDTH-1):0]		x_in_buf;
	reg		signed	[(CORDIC_WIDTH-1):0]		y_in_buf;
	reg											reverse_flag;
	
	reg					[(COUNT_WIDTH-1):0]		control_count;
	
	wire										direction;
	reg		signed		[(CORDIC_WIDTH+1):0]	x_reg;
	reg		signed		[(CORDIC_WIDTH+1):0]	y_reg;
	reg					[(CORDIC_WIDTH-1):0]	q_reg;
	
	wire	signed		[(CORDIC_WIDTH+1):0]	x_shift [(CORDIC_WIDTH-1):0];
	wire	signed		[(CORDIC_WIDTH+1):0]	y_shift [(CORDIC_WIDTH-1):0];
	wire				[(CORDIC_WIDTH-1):0]	q_shift;
	
	reg					[63:0]					phase_value;

	always @(*) begin
		case(control_count)	// These are generated by the function (2^64)*atan(2^(-i))/(2*PI)
			'h00 : phase_value = 64'h0000000000000000;	
			'h01 : phase_value = 64'h0000000000000000;	
			'h02 : phase_value = 64'h2000000000000000;	
			'h03 : phase_value = 64'h12E4051D9DF30800;	
			'h04 : phase_value = 64'h09FB385B5EE39E80;	
			'h05 : phase_value = 64'h051111D41DDD9A40;
			'h06 : phase_value = 64'h028B0D430E589B00;
			'h07 : phase_value = 64'h0145D7E159046280;
			'h08 : phase_value = 64'h00A2F61E5C282630;
			'h09 : phase_value = 64'h00517C5511D442B0;
			'h0A : phase_value = 64'h0028BE5346D0C338;
			'h0B : phase_value = 64'h00145F2EBB30AB38;
			'h0C : phase_value = 64'h000A2F980091BA7C;
			'h0D : phase_value = 64'h000517CC14A80CB7;
			'h0E : phase_value = 64'h00028BE60CDFEC62;
			'h0F : phase_value = 64'h000145F306C172F2;
			'h10 : phase_value = 64'h0000A2F9836AE911;
			'h11 : phase_value = 64'h0000517CC1B6BA7C;
			'h12 : phase_value = 64'h000028BE60DB85FC;
			'h13 : phase_value = 64'h0000145F306DC816;
			'h14 : phase_value = 64'h00000A2F9836E4AE;
			'h15 : phase_value = 64'h00000517CC1B726B;
			'h16 : phase_value = 64'h0000028BE60DB938;
			'h17 : phase_value = 64'h00000145F306DC9C;
			'h18 : phase_value = 64'h000000A2F9836E4E;
			'h19 : phase_value = 64'h000000517CC1B727;
			'h1A : phase_value = 64'h00000028BE60DB94;
			'h1B : phase_value = 64'h000000145F306DCA;
			'h1C : phase_value = 64'h0000000A2F9836E5;
			'h1D : phase_value = 64'h0000000517CC1B72;
			'h1E : phase_value = 64'h000000028BE60DB9;
			'h1F : phase_value = 64'h0000000145F306DD;
			'h20 : phase_value = 64'h00000000A2F9836E;
			'h21 : phase_value = 64'h00000000517CC1B7;
			'h22 : phase_value = 64'h0000000028BE60DC;
			'h23 : phase_value = 64'h00000000145F306E;
			'h24 : phase_value = 64'h000000000A2F9837;
			'h25 : phase_value = 64'h000000000517CC1B;
			'h26 : phase_value = 64'h00000000028BE60E;
			'h27 : phase_value = 64'h000000000145F307;
			'h28 : phase_value = 64'h0000000000A2F983;
			'h29 : phase_value = 64'h0000000000517CC2;
			'h2A : phase_value = 64'h000000000028BE61;
			'h2B : phase_value = 64'h0000000000145F30;
			'h2C : phase_value = 64'h00000000000A2F98;
			'h2D : phase_value = 64'h00000000000517CC;
			'h2E : phase_value = 64'h0000000000028BE6;
			'h2F : phase_value = 64'h00000000000145F3;
			'h30 : phase_value = 64'h000000000000A2FA;
			'h31 : phase_value = 64'h000000000000517D;
			'h32 : phase_value = 64'h00000000000028BE;
			'h33 : phase_value = 64'h000000000000145F;
			'h34 : phase_value = 64'h0000000000000A30;
			'h35 : phase_value = 64'h0000000000000518;
			'h36 : phase_value = 64'h000000000000028C;
			'h37 : phase_value = 64'h0000000000000146;
			'h38 : phase_value = 64'h00000000000000A3;
			'h39 : phase_value = 64'h0000000000000051;
			'h3A : phase_value = 64'h0000000000000029;
			'h3B : phase_value = 64'h0000000000000014;
			'h3C : phase_value = 64'h000000000000000A;
			'h3D : phase_value = 64'h0000000000000005;
			'h3E : phase_value = 64'h0000000000000003;
			'h3F : phase_value = 64'h0000000000000001;
			'h40 : phase_value = 64'h0000000000000001;
			default : phase_value = 64'h0000000000000000;
		endcase
	end
	
	assign q_shift[(CORDIC_WIDTH-1):0] = phase_value[63:(64-CORDIC_WIDTH)];
	

	//------------------------------------------------------
	//--				CORDIC Control				 	  --
	//------------------------------------------------------
	always @(posedge clk_in or negedge _reset_in) begin 
		if (!_reset_in) begin
			control_count <= {COUNT_WIDTH{1'b0}};
		end
		else begin
			if (done_out)
				control_count <= {COUNT_WIDTH{1'b0}};
			else if ((start_in)||(|control_count))
				control_count <= control_count + 1'b1;
		end
	end	
	
	
	always @(posedge clk_in or negedge _reset_in) begin 
		if (!_reset_in) begin
			x_in_buf <= {CORDIC_WIDTH{1'b0}};
			y_in_buf <= {CORDIC_WIDTH{1'b0}};
			reverse_flag <= 1'b0;
		end
		else if ((start_in)&&(!(|control_count))) begin
			if (x_data_in[CORDIC_WIDTH-1]) begin	// Is it negative? If so rotate by 180.
				x_in_buf <= (|x_data_in[(CORDIC_WIDTH-2):0]) ? (~x_data_in[(CORDIC_WIDTH-1):0] + 1'b1) : {1'b0, {(CORDIC_WIDTH-1){1'b1}}};
				y_in_buf <= ((!y_data_in[CORDIC_WIDTH-1]) || (|y_data_in[(CORDIC_WIDTH-2):0])) ? (~y_data_in[(CORDIC_WIDTH-1):0] + 1'b1) : {1'b0, {(CORDIC_WIDTH-1){1'b1}}};
				reverse_flag <= 1'b1;
			end
			else begin
				x_in_buf <= x_data_in;
				y_in_buf <= y_data_in;
				reverse_flag <= 1'b0;
			end
		end
	end
	
	assign done_out = (control_count == (CORDIC_WIDTH + 2));

	generate
	for (i=0; i < CORDIC_WIDTH; i=i+1) begin : g_cordic
		if (i==0) begin
			assign x_shift[i] = (control_count<=2) ? x_reg : {(CORDIC_WIDTH+2){1'b0}};
			assign y_shift[i] = (control_count<=2) ? y_reg : {(CORDIC_WIDTH+2){1'b0}};
		end
		else begin
			assign x_shift[i][(CORDIC_WIDTH+1):0] = ((control_count==(i+2)) ? {{i{x_reg[CORDIC_WIDTH+1]}}, x_reg[(CORDIC_WIDTH+1):i]} : {(CORDIC_WIDTH+2){1'b0}}) | x_shift[i-1][(CORDIC_WIDTH+1):0];
			assign y_shift[i][(CORDIC_WIDTH+1):0] = ((control_count==(i+2)) ? {{i{y_reg[CORDIC_WIDTH+1]}}, y_reg[(CORDIC_WIDTH+1):i]} : {(CORDIC_WIDTH+2){1'b0}}) | y_shift[i-1][(CORDIC_WIDTH+1):0];
		end
	end
	endgenerate
	
	assign direction = y_reg[CORDIC_WIDTH+1];	


	//------------------------------------------------------
	//--					CORDIC 					 	  --
	//------------------------------------------------------
	always @(posedge clk_in or negedge _reset_in) begin 
		if (!_reset_in) begin
			x_reg <= {(CORDIC_WIDTH+2){1'b0}};
			y_reg <= {(CORDIC_WIDTH+2){1'b0}};
			q_reg <= {CORDIC_WIDTH{1'b0}};
		end
		else begin
			if (control_count <= 1) begin
				x_reg <= x_in_buf;
				y_reg <= y_in_buf;
				q_reg <= q_shift;
			end
			else begin
				x_reg[(CORDIC_WIDTH+1):0] <= (direction ? (x_reg - y_shift[CORDIC_WIDTH-1]) : (x_reg + y_shift[CORDIC_WIDTH-1]));
				y_reg[(CORDIC_WIDTH+1):0] <= (direction ? (y_reg + x_shift[CORDIC_WIDTH-1]) : (y_reg - x_shift[CORDIC_WIDTH-1]));
				q_reg[(CORDIC_WIDTH-1):0] <= (direction ? (q_reg - q_shift) : (q_reg + q_shift));
			end
		end
	end

	assign q_data_out = reverse_flag ? {1'b1, {(CORDIC_WIDTH-1){1'b0}}} + q_reg : q_reg;

	
	// Ceil Log 2 Function
	function integer clogb2;input [31:0] value; 
		begin
			value = value - 1'b1;
			for (clogb2=0; value>0; clogb2=clogb2+1)  value = value>>1;
		end
	endfunction
	
endmodule
